/**
 * 
 */
package com.feib.stms.batch.job;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import org.apache.commons.collections.CollectionUtils;

import com.feib.stms.batch.domain.CodeTable;
import com.feib.stms.batch.domain.DeliveryPaymentChkSum;
import com.feib.stms.batch.domain.MessageQueue;
import com.feib.stms.batch.domain.UploadFile;
import com.feib.stms.batch.service.CodeTableService;
import com.feib.stms.batch.service.DeliveryPaymentChkSumMQService;
import com.feib.stms.batch.service.MQService;
import com.feib.stms.batch.service.MessageQueueService;

/**
 * @author Jimmy Liu
 *
 */
@Service("deliveryPaymentChkSumMQJob")
public class DeliveryPaymentChkSumMQJob {

	public final static Logger logger = LoggerFactory.getLogger(DeliveryPaymentChkSumMQJob.class);

	public static final SimpleDateFormat BASIC_DATE_TIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

	private static ApplicationContext SPRING_CTX = null;

	/**
	 * 驗資正常代碼
	 */
	private static final String RT_OK = "P011";

	@Resource(name="mqService")
	private MQService mqService;

	@Resource(name="deliveryPaymentChkSumMQService")
	private DeliveryPaymentChkSumMQService deliveryPaymentChkSumMQService;

	@Resource(name="codeTableService")
	private CodeTableService codeTableService;

	@Resource(name="messageQueueService")
	private MessageQueueService messageQueueService;

	@Resource(name="txManager")
	private PlatformTransactionManager transactionManager;



	/**
	 * 一次抓取驗資檔案明細最多筆數(避免一次抓取過大的明細，影響效能)
	 */
	private int maxRecords = 300;

	/**
	 * 第1次驗資: true<br/>
	 * 第2次驗資: false<br/>
	 * 由Main主程式設定第一次驗資或是第二次驗資
	 */
	private boolean firstTime = true;

	/**
	 * 驗資成功
	 */
	private boolean  success  = false;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		SPRING_CTX = new ClassPathXmlApplicationContext(
				new String[] {"applicationContext-config.xml",	"applicationContext-dao.xml", "applicationContext-properties.xml"});


		Date start = new Date();
		logger.info("[開始] XXXXXXXXX  - {}", BASIC_DATE_TIME_FORMAT.format(start));

		try 
		{
			DeliveryPaymentChkSumMQJob job = (DeliveryPaymentChkSumMQJob)SPRING_CTX.getBean("deliveryPaymentChkSumMQJob");

			// 第一個參數 >1 時代表 第2次驗資
			if (0 != args.length){
				try 
				{
					if (1 < Integer.parseInt(args[0]))
						job.setFirstTime(false); // 第2次驗資
					job.execute();
				}
				catch (Exception e)
				{
					logger.error(e.getMessage(), e);
					throw new RuntimeException("invalide frist argument, must be 1 or 2");
				}
			}

		}
		catch (Exception e)
		{
			logger.error(e.getMessage(), e);
		}
		finally
		{
			Date end = new Date();
			logger.info("[結束] XXXXXXXXXX  - {}", BASIC_DATE_TIME_FORMAT.format(end));
			logger.info("共費時 - " + ((end.getTime() - start.getTime()) / 1000) + " 秒");

		}

	}

	/**
	 * 執行批次
	 * @throws Exception 
	 */
	public void execute() throws Exception
	{

		Date SysDate=new  Date();
		logger.info("系統第{}次驗資即時電文批次  時間 ：{}",firstTime?1:2, SysDate);

		/* 取得下一工作日日期 */
		Date nextBussinessDay =deliveryPaymentChkSumMQService.getNextBusinessDay(SysDate);


		if(null==nextBussinessDay){
			success=false;
			throw new Exception("無法取得系統日:"+SysDate+"之下一工作日日期");
		}

		
		logger.info("取得下一工作日日期：{}", nextBussinessDay);

		//TODO 可能需將過期資料回寫 目前為調整審查放行的撈取方式(不撈取交割日期小於今天的資料)

		/* 取得尚未驗資資料  檔案 */


		List<UploadFile>  chkUploadFiles  = deliveryPaymentChkSumMQService.queryToDoChkUploadFile(nextBussinessDay, firstTime);

		if(CollectionUtils.isEmpty(chkUploadFiles)){

			logger.info("無法取得交割日期大於等於下一工作日{}的驗資資料",nextBussinessDay);
			success=false;
		}else{

			int toDoRecords = 0; // 檔案明細中，驗資尚未通過的總筆數

			for(UploadFile chkUploadFile: chkUploadFiles)
			{
				int begIdx = 0;
				int endIdx = maxRecords - 1;
				Long uploadFilePk=chkUploadFile.getUploadFilePk();
				logger.info(" 取得檔案 UPLOAD_FILE_PK：{}", uploadFilePk);

				/* 逐個檔案處理，若檔案有問題，跳過繼續執行下一個 */
				try {
					List<DeliveryPaymentChkSum> toDos = null;

					toDoRecords = deliveryPaymentChkSumMQService.countAllToDoRecords(uploadFilePk); // 檔案明細中，驗資尚未通過的總筆數

					while(0 != toDoRecords && begIdx < toDoRecords) 
					{					
						toDos = deliveryPaymentChkSumMQService.findToDoRecords(uploadFilePk, begIdx, endIdx);

						for(DeliveryPaymentChkSum toDo : toDos)
						{

							logger.info(" 取得DeliveryPaymentChkSum  UPLOAD_FILE_PK{}：getNetAmount{}", toDo.getUploadFilePk(),toDo.getNetAmount());
							/* 若足額就不必驗 狀態要更新成什麼? TODO   */
							if(0 < toDo.getNetAmount().intValue()){
								// 逐筆明細處理
								processSingleRecord(toDo);
							}

						}

						// 設定下一次的開始跟結束
						begIdx = begIdx + maxRecords;
						endIdx = begIdx + maxRecords - 1;

						// free resource
						if (null != toDos)
							toDos.clear();
						toDos = null;
						
						if(firstTime){
							/* 1. 第一次驗資中(驗資檔) */	
							chkUploadFile.setFileStatus(1);

						}else{
							/* 5. 第二次驗資中(驗資檔) */	
							chkUploadFile.setFileStatus(5);
						}
					}


				} catch (Exception e) {
					logger.error("驗資錯誤(execute)", e);
					logger.error("驗資發生錯誤，請先排除問題，再修改驗資檔案(UPLOAD_FILE.UPLOAD_FILE_PK = {}) 的狀態後，重新執行", uploadFilePk);
					/* 驗資失敗 */
					success=false;
				}
				finally
				{


					chkUploadFile.setUpdatedDate(new Date());
					chkUploadFile.setUpdatedBy("BATCH");

					deliveryPaymentChkSumMQService.updateUploadFileFileStatus(chkUploadFile);

				}

				/* 成功後*/
				if(!success){
					/*  3. 第一次驗資完成(驗資檔 */
					chkUploadFile.setFileStatus(3);

				}else{
					/*  7. 已驗資(驗資檔) */
					chkUploadFile.setFileStatus(7);
				}

				deliveryPaymentChkSumMQService.updateUploadFileFileStatus(chkUploadFile);


			}



			if (null != chkUploadFiles)
				chkUploadFiles.clear(); // free resource
			chkUploadFiles = null;

		}

		logger.info("execute：END");
	}

	/**
	 * 
	 * @param deliveryPaymentChkSum
	 */
	private void processSingleRecord(DeliveryPaymentChkSum deliveryPaymentChkSum) {


		logger.info(" processSingleRecord 處理 驗資彙總檔    ");

		//		 建立交易的定義，每一次都是新的 Transaction
		DefaultTransactionDefinition definition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

		TransactionStatus status = this.transactionManager.getTransaction(definition);


		try
		{
			String rtChkMsgId = null; // 即時電文序號，若已存在，則使用原本的，否則產生一組新的
			String rtChkResult = null; // 即時電文回覆結果
			Date rtChkOutDate = new Date(); // 發送電文日期時間
			Date rtChkInDate = null; // 接收電文日期時間

			//deliveryPaymentChkSum.getDealResultCode();

			String kinbr=deliveryPaymentChkSum.getAccountNo().substring(0, 3);

			if (this.firstTime)
			{
				// 第一次驗資
				rtChkMsgId = getNewMessageID(kinbr);
				deliveryPaymentChkSum.setRtChkMsgId1(rtChkMsgId);

				deliveryPaymentChkSum.setRtChkOutDate1(rtChkOutDate);
			}
			else
			{
				// 第二次驗資
				rtChkMsgId = getNewMessageID(kinbr);
				deliveryPaymentChkSum.setRtChkMsgId2(rtChkMsgId);

				deliveryPaymentChkSum.setRtChkOutDate2(rtChkOutDate);
			}

			// 組 送出去的電文 TITA
			String myTITA = genMqTita(deliveryPaymentChkSum, rtChkMsgId);

			logger.debug("myTITA {}",myTITA);

			logger.debug("myTITA.length() {}",myTITA.length());

			// 收到回傳電文
			String myTOTA;
			try {
				myTOTA = this.sendRequestToMQ(myTITA, rtChkMsgId, deliveryPaymentChkSum.getDeliveryPaymentChkSumPk(), UploadFile.UPLOAD_FILE_DELIVERY_PAYMENT_CHK);
			}catch (Exception e) {            
				logger.error(e.getMessage(),  e);            
				transactionManager.rollback(status);
				status = null; // roll back 不需要再呼叫 commit
				/* 驗資失敗 */
				success=false;
				throw new RuntimeException(e);				
			}
			if (null != status)
				transactionManager.commit(status);

			logger.debug("myTOTA {}",myTOTA);

			logger.debug("myTOTA.length() {}",myTOTA.length());


			rtChkInDate = new Date();

			/* 拆解結果 */
			/* 訊息類別	MTYPE	X(1)	R	P   */
			/* 訊息編號	MSGNO	9(3)	R	011 */
			/* 訊息長度	MSGLNG	9(3)	R	558 */

			rtChkResult = new String(myTOTA.substring(15,19));//需確認
			deliveryPaymentChkSum.setDealResultCode(rtChkResult);
			if (this.firstTime)
			{
				deliveryPaymentChkSum.setRtChkResult1(rtChkResult);
				deliveryPaymentChkSum.setRtChkInDate1(rtChkInDate);
			}
			else
			{
				deliveryPaymentChkSum.setRtChkResult2(rtChkResult);
				deliveryPaymentChkSum.setRtChkInDate2(rtChkInDate);
			}

			if (RT_OK.equalsIgnoreCase(rtChkResult))
			{
				/* 可抵用餘額	AVBAL	S9(11)V99	R	ACTBAL */
				BigDecimal accountBalance =new BigDecimal(new String(myTOTA.substring(58,69)+"."+myTOTA.substring(69,71))).setScale(2, BigDecimal.ROUND_HALF_UP);
				deliveryPaymentChkSum.setAccountBalance(accountBalance);
				deliveryPaymentChkSum.setLessAmount(new BigDecimal(0));
				logger.debug(" myTOTA accountBalance", accountBalance);
			}
			else
			{
				// TODO: 需紀錄不足金額 (netAmount - lessAmount);	可抵用餘額	HWBAL	PIC -ZZ,ZZZ,ZZZ,ZZZ.99
				BigDecimal accountBalance = new BigDecimal(myTOTA.substring(32,43)+"."+myTOTA.substring(43,45)).setScale(2, BigDecimal.ROUND_HALF_UP);//
				deliveryPaymentChkSum.setAccountBalance(accountBalance);
				logger.debug(" myTOTA accountBalance", accountBalance);
				BigDecimal lessAmount= accountBalance.subtract(deliveryPaymentChkSum.getNetAmount());
				deliveryPaymentChkSum.setLessAmount(lessAmount);
			}
			/* 驗資成功 */
			success=true;

		} 
		catch (Exception e) 
		{
			deliveryPaymentChkSum.setDealResultCode("99");
			logger.error("驗資錯誤(processSingleRecord)", e);  
			/* 驗資失敗 */
			success=false;
			throw new RuntimeException(e);
		}
		finally
		{
			/* 將驗資結果更新至資料庫中 */

			status = this.transactionManager.getTransaction(definition);

			try {
				this.deliveryPaymentChkSumMQService.updateDeliveryPaymentChkSum(deliveryPaymentChkSum);
			}
			catch (Exception e) {            
				logger.error(e.getMessage(),  e);            
				transactionManager.rollback(status);
				status = null; // roll back 不需要再呼叫 commit
				/* 驗資失敗 */
				success=false;
				throw new RuntimeException(e);
			}
			if (null != status)
				transactionManager.commit(status);
		}


	}

	/**
	 * 產生MQ交易電文
	 * @param deliveryPaymentChkSum
	 * @return
	 */
	private String genMqTita(DeliveryPaymentChkSum deliveryPaymentChkSum, String rtChkMsgId) {
		StringBuffer result = new StringBuffer();

//		TODO 組電文   1.	台幣存摺存款餘額查詢  TITA BASIC LABEL      LENGTH = 72 
		//result.append(deliveryPaymentChkSum.getAccountNo().substring(0, 3)); //輸入行別	KINBR 9(3) TODO
		//result.append(87);//櫃台機序號	TRMSEQ	9(2)	R	87
		result.append(rtChkMsgId);//交易傳輸編號	TXTNO	9(5)	R	00000 		
		result.append("PI");//端末 TASKID	TTSKID	X(2)	R	PI
		result.append(0);//櫃台機種類	TRMTYP	9(1)	R	0
		result.append("P00");//交易別代號	TXCD	X(3)	R	P00
		result.append(0);//處理型態	PTYPE	9(1)	R	0
		result.append("008");//交易摘要	DSCPT1	9(3)	R	008
		result.append(0);//更正記號	HCODE	9(1)	R	0
		result.append(0);//補送記號	YCODE	9(1)	R	0
		result.append(deliveryPaymentChkSum.getAccountNo());//帳  號	ACTNO	9(14)	R
		result.append("  ");//帳務別	TXTYPE	X(2)	R	“ “  TODO check
		result.append(0);//借貸別	CRDB	9(1)	R	0
		result.append("00");//主管許可記號	SPCD	9(2)	R	0 TODO check
		result.append(0);//無摺記號	NBCD	9(1)	R	0
		result.append(88);//櫃員編號	TLRNO	9(2)	R	88
		result.append(0);//訓練代登	TRNSBT	9(1)	R	0
		result.append("00");//幣  別	CURCD	9(2)	R	0  TODO check

		BigDecimal  amount    =deliveryPaymentChkSum.getNetAmount();
		amount=amount.setScale(2, BigDecimal.ROUND_UNNECESSARY); //小數點設定兩位,不做四捨五入
		int  amountLen= String.valueOf(amount).length()-3;//去掉小數點跟小數點後兩位的長度

		if(11 >amountLen){

			for(int i=1;i<=(11-amountLen);i ++){
				result.append("0");
			}

		}

		result.append(amount.toString().replace(".", ""));//交易金額	TXAMT	9(11)V99	R

		result.append("00000000000");//FILLER	X(11)	X	0 
		result.append(1);//交易形態	MODE	9(01)	R	TODO *交易形態 : 1 - ON-LINE   2 - HALF-ONLINE check
		result.append("01");//交易項目	ITEMCD	9(02)	R	00:申購股票驗資 01:交割股款驗資   02:餘額查詢
		result.append(deliveryPaymentChkSum.getCustomerId());//身份證號	CIFKEY	X(10)	X	
		result.append(0);//身份證號檢碼	CIFKEYERR	X(01)	X	DEFUAL=0

		return result.toString();
	}




	/**
	 * 產生新的電文序號<br/>
	 * @param kinbr TODO
	 * @return
	 */
	private String getNewMessageID(String kinbr)
	{
		CodeTable code=new CodeTable();
		StringBuffer messageID = new StringBuffer();
		int txtno=0;//五碼00000

		code=this.codeTableService.getCodeTable("MessageID", kinbr);
		messageID.append(kinbr).append("87");

		if(null!=code){
			txtno=Integer.parseInt(code.getCodeName());
			int txtnoLen=code.getCodeName().length();
			if(5 >txtnoLen){

				for(int i=1;i<=(5-txtnoLen);i ++){
					messageID.append("0");
				}

			}
			messageID.append(txtno);
			code.setCodeName(String.valueOf(txtno+1));
			codeTableService.update(code);

		}else{
			/*預設值*/
			code=new CodeTable();
			messageID.append("00001");
			code.setCodeKind("MessageID");
			code.setCodeValue(kinbr);
			code.setCodeName("2");
			code.setCreatedBy("BATCH");
			code.setCreatedDate(new Date());

			codeTableService.insert(code);

		}	

		logger.debug("MessageID {}",messageID.toString());

		return messageID.toString();
	}

	/**
	 * 傳送電文
	 * @param myTITA 送出去電文
	 * @param myMessageID 電文序號
	 * @param paymentPk TODO
	 * @param fileFormat TODO
	 * @return 收到的電文
	 */
	private String sendRequestToMQ(String myTITA, String myMessageID, Long paymentPk, String fileFormat)
	{
		logger.info("SendMQ starting...");

		logger.info("MQ TITA: {}, MessageID: {}", myTITA, myMessageID);

		// TODO: 紀錄發送電文於資料庫中

		MessageQueue messageQueue=	new MessageQueue();

		messageQueue.setMessageId(myMessageID);
		messageQueue.setMqContext(myTITA);
		messageQueue.setPaymentPk(paymentPk);//TODO
		messageQueue.setFileFormat(fileFormat);
		messageQueue.setSendTime(new Date());
		//type
		this.messageQueueService.insert(messageQueue);
		
		messageQueue=this.messageQueueService.findMessageQueueByMessageId(myMessageID);

		String totaStr = null; //MQ 回傳的電文
		try {

		/*	if ("1".equals(MQService.MQonline))
			{
				 MQ Online  
				mqService.send(myTITA, MQService.MSG_TYPE_NONEXML, myMessageID, "");
			}
			else
			{  
				 Fake MQ 模擬電文 
				mqService.send(myTITA, MQService.MSG_TYPE_NONEXML, myMessageID, "TOTA.txt");
			}

			messageQueue.setReturnTime(new Date());
			totaStr = mqService.rcv(myMessageID);
*/
			totaStr="0028700017PI011P011356101081000+000005986640000000000000000000059866400+0000059866400                                       1010628             0000000000000                                                                                 0000000000000                   +0000059866400                      0000000000+0000000000000+0000000000000000000000000";
				
			logger.info("MQ TOTA: {}" + totaStr);

		} catch (Exception e) {

			logger.error("[電文傳送錯誤]", e);
			/* 驗資失敗 */
			success=false;
			totaStr = null;
		}
		finally
		{


			messageQueue.setMqResult(totaStr);
			
			messageQueue.setMqResult("R");
		

			//messageQueue.setMqResult(totaStr);
			
			this.messageQueueService.update(messageQueue);
		}

		return totaStr;
	}

	public void setFirstTime(boolean firstTime) {
		this.firstTime = firstTime;
	}

}
